package com.authsphere.bootstrap.web.configuration;

import com.authsphere.bootstrap.web.user.WebUserDetailsService;
import com.authsphere.logging.common.collector.LogCollector;
import com.authsphere.logging.common.entity.LogDetail;
import com.authsphere.security.authorization.AuthSphereResponseAuthenticationSuccessHandler;
import com.authsphere.security.authorization.AuthorizationTokenAuthenticationProvider;
import com.authsphere.security.authorization.AuthorizationTokenConfigurer;
import com.authsphere.security.authorization.PersistentTokenRedisRememberMeServices;
import com.authsphere.security.common.handler.ResponseResultAuthenticationFailureHandler;
import com.authsphere.security.common.handler.ResponseResultAuthenticationSuccessHandler;
import com.authsphere.security.configurer.password.configurers.UsernamePasswordAuthenticationFilterConfigurer;
import com.authsphere.security.configurer.social.configurers.ThirdAuthorizationAuthenticationFilterConfigurer;
import com.authsphere.security.configurer.verifycode.configurers.VerificationCodeAuthenticationFilterConfigurer;
import com.authsphere.security.token.common.AuthenticationTokenServices;
import com.authsphere.security.web.configuration.SecurityWebConfig;
import com.authsphere.security.web.handler.AuthenticationHandler;
import com.authsphere.security.web.session.strategy.CsrfTokenSessionAuthenticationStrategy;
import jakarta.servlet.Filter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

import java.util.List;

/**
 * @program: AuthSphere
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/11 11:16
 **/
@Configuration
@EnableWebSecurity
public class WebAuthSphereSecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity
            , final ObjectProvider<AuthenticationTokenServices> authenticationTokenServices
            , final ObjectProvider<LogCollector<LogDetail>> logCollectorObjectProvider
            , final ObjectProvider<SecurityWebConfig> configProvider
            , final ObjectProvider<AuthenticationHandler> authenticationHandlerProvider) throws Exception {
        httpSecurity.csrf().disable()
                .sessionManagement().disable();

        SecurityWebConfig config = configProvider.getIfAvailable(SecurityWebConfig::new);

        LogCollector<LogDetail> logCollector = logCollectorObjectProvider.getIfAvailable();
        ResponseResultAuthenticationSuccessHandler successHandler = new ResponseResultAuthenticationSuccessHandler();
        ResponseResultAuthenticationFailureHandler failureHandler = new ResponseResultAuthenticationFailureHandler();
        if (logCollector != null) {
            successHandler.setLogCollector(logCollector);
            failureHandler.setLogCollector(logCollector);
        }
        AuthenticationTokenServices tokenServices = authenticationTokenServices.getIfAvailable();
        if (tokenServices != null) {
            httpSecurity.setSharedObject(AuthenticationTokenServices.class, tokenServices);

            PersistentTokenRedisRememberMeServices rememberMeServices = getPersistentTokenRedisRememberMeServices(config, tokenServices);
            httpSecurity.setSharedObject(RememberMeServices.class, rememberMeServices);
        }
        AuthenticationHandler authenticationHandler = authenticationHandlerProvider.getIfAvailable();
        if (authenticationHandler != null) {
            httpSecurity.setSharedObject(AuthenticationHandler.class, authenticationHandler);
        }
        // 设置csrfToken
        CsrfTokenSessionAuthenticationStrategy sessionAuthenticationStrategy = buildSessionAuthenticationStrategy(config);
        httpSecurity.setSharedObject(SessionAuthenticationStrategy.class, sessionAuthenticationStrategy);
        httpSecurity
                .apply(new UsernamePasswordAuthenticationFilterConfigurer<>())
                .processingUrl("/pwd/login")
                .successHandler(successHandler)
                .failureHandler(failureHandler)
                .and()
                .apply(new VerificationCodeAuthenticationFilterConfigurer<>())
                .processingUrl("/sms/login")
                .successHandler(successHandler)
                .failureHandler(failureHandler)
                .and()
                .apply(new ThirdAuthorizationAuthenticationFilterConfigurer<>())
                .processingUrl("/third/login")
//                .bindRedirectUrl("https://localhost:8080/backend/third/bind")
                .successHandler(successHandler)
                .failureHandler(failureHandler)
                .and()
                .apply(new AuthorizationTokenConfigurer<>())
                .processingUrl("/authorization")
                .successHandler(new AuthSphereResponseAuthenticationSuccessHandler())
                .failureHandler(failureHandler)
                .and()
                .authorizeHttpRequests().anyRequest().permitAll();
        DefaultSecurityFilterChain chain = httpSecurity.build();
        List<Filter> filters = chain.getFilters();
        List<Filter> list = filters.stream().distinct().toList();
        return new DefaultSecurityFilterChain(chain.getRequestMatcher(), list);
    }

    private PersistentTokenRedisRememberMeServices getPersistentTokenRedisRememberMeServices(SecurityWebConfig config, AuthenticationTokenServices tokenServices) {
        PersistentTokenRedisRememberMeServices rememberMeServices = new PersistentTokenRedisRememberMeServices(new AuthorizationTokenAuthenticationProvider(tokenServices));
        SecurityWebConfig.RememberMeConfig rememberMeConfig = config.getRememberMeConfig();
        rememberMeServices.setAlwaysRemember(rememberMeConfig.getAlwaysRemember());
        rememberMeServices.setCookieDomain(rememberMeConfig.getCookieDomain());
        rememberMeServices.setCookieName(rememberMeConfig.getCookieName());
        rememberMeServices.setParameter(rememberMeConfig.getParameterName());
        rememberMeServices.setUseSecureCookie(rememberMeConfig.getCookieSecure());
        rememberMeServices.setAutoRenewal(rememberMeConfig.getAutoRenewal());
        rememberMeServices.setRenewalInterval(rememberMeConfig.getRenewalInterval());
        return rememberMeServices;
    }

    private CsrfTokenSessionAuthenticationStrategy buildSessionAuthenticationStrategy(SecurityWebConfig config) {
        CookieCsrfTokenRepository tokenRepository = new CookieCsrfTokenRepository();
        SecurityWebConfig.CsrfTokenConfig csrfTokenConfig = config.getCsrfTokenConfig();
        tokenRepository.setCookieMaxAge(csrfTokenConfig.getCookieMaxAge());
        tokenRepository.setSecure(csrfTokenConfig.getCookieSecure());
        tokenRepository.setCookiePath(csrfTokenConfig.getCookiePath());
        tokenRepository.setCookieDomain(csrfTokenConfig.getCookieDomain());
        tokenRepository.setCookieName(csrfTokenConfig.getCookieName());
        tokenRepository.setCookieHttpOnly(csrfTokenConfig.getCookieHttpOnly());
        tokenRepository.setHeaderName(csrfTokenConfig.getHeaderName());
        tokenRepository.setParameterName(csrfTokenConfig.getParameterName());
        return new CsrfTokenSessionAuthenticationStrategy(tokenRepository);
    }

    @Bean
    public UserDetailsService webUserDetailsService() {
        return new WebUserDetailsService();
    }
}
